﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Heaven.Texturing.Importers.DirectDraw
{
    public enum DDSTypes
    {
        Map2D,
        Map3D,
        CubeMap
    };

    public class DirectDrawFile
    {        
        #region Атрибуты

        private Header header;

        // Двухмерная текстура
        private PlanarTexture planarTexture;
        // Трехмерная текстура
        private Map3D map3d;
        // Кубемап
        private CubicTexture cubeMap;
        // Тип текстуры
        private DDSTypes type;

        #endregion

        #region Свойства

        /// <summary>
        /// Двухмерная текстура
        /// </summary>
        public PlanarTexture PlanarTexture
        {
            get { return planarTexture; }
        }

        /// <summary>
        /// Трехмерная текстура
        /// </summary>
        public Map3D Map3D
        {
            get { return map3d; }
        }

        /// <summary>
        /// Кубемап
        /// </summary>
        public CubicTexture CubicTexture
        {
            get { return cubeMap; }
        }

        /// <summary>
        /// Тип текстуры
        /// </summary>
        public DDSTypes Type
        {
            get { return type; }
        }

        #endregion

        #region Инициализация

        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="fileName">Имя файла</param>
        public DirectDrawFile(string fileName)
        {
            using(Stream stream = new FileStream(fileName,FileMode.Open,FileAccess.Read))
            {
                Load(stream);
            }
        }

        /// <summary>
        /// Конструктор
        /// </summary>
        /// <param name="stream">Поток с данными</param>
        public DirectDrawFile(Stream stream)
        {
            Load(stream);
        }

        #endregion

        #region Внутренее

        // Загрузка текстуры из потока
        private void Load(Stream stream)
        {
            BinaryReader reader = new BinaryReader(stream);
            // Загружаем метку FourCC
            char[] fourCC = reader.ReadChars(4);
            // Загружаем заголовок
            header = new Header(reader.ReadBytes(124));
            // Определяем тип текстуры
            if(((header.Flags&Header.DDSD_DEPTH) !=0 )&&((header.Caps.Caps2&Header.DDSCAPS2_VOLUME)!=0 ))
            {
                // Volume - текстура
                type = DDSTypes.Map3D;
                map3d = new Map3D(header, reader);
            }
            else if (((header.Caps.Caps1 & Header.DDSCAPS_COMPLEX) != 0) && ((header.Caps.Caps2 & Header.DDSCAPS2_CUBEMAP) != 0))
            {
                // Cubemap
                type = DDSTypes.CubeMap;
                cubeMap = LoadCubeMap(header, reader);
            }
            else
            {
                // Обычная текстура
                type = DDSTypes.Map2D;
                planarTexture = LoadPlanarTexture(header, reader);
            }
            reader.Close();
        }

        /// <summary>
        /// Загрузка двухмерной текстуры
        /// </summary>
        /// <param name="header">Заголовок</param>
        /// <param name="reader">Ридер с данными</param>
        PlanarTexture LoadPlanarTexture(Header header, BinaryReader reader)
        {
            // Извлекаем нужные данные из заголовка
            int width = header.Width;
            int height = header.Height;
            bool hasMipMaps = (((header.Flags & Header.DDSD_MIPMAPCOUNT) != 0) && ((header.Caps.Caps1 & Header.DDSCAPS_MIPMAP) != 0) && ((header.Caps.Caps1 & Header.DDSCAPS_COMPLEX) != 0));
            int mipMapsCount = 0;
            if (hasMipMaps) mipMapsCount = header.MipMapCount - 1;

            PlanarLayer mainLayer = null;
            PlanarLayer[] textures = new PlanarLayer[mipMapsCount];

            // Определем тип компресии
            if ((header.PixelFormat.Flags & Header.DDPF_FOURCC) == 0)
            {
                // Без компресии
                // Каждый следующий мипмап в два раза меньше предыдущего                

                // Проверяем наличие питча
                int pitch = header.PitchOrLinearSize;
                if ((header.Flags & Header.DDSD_PITCH) == 0) pitch = width * header.PixelFormat.RgbBitCount / 8;

                // Конвертим форматы
                LayerConverter textureData = new LayerConverter(reader.ReadBytes(pitch * height),header.PixelFormat);
                // Грузим основную карту
                mainLayer = new PlanarLayer(textureData.Data, width, height, textureData.Format);
                // Грузим мипы
                for (int i = 0; i < mipMapsCount; i++)
                {
                    width /= 2;
                    height /= 2;
                    if (width == 0) width = 1;
                    if (height == 0) height = 1;
                    pitch = width * header.PixelFormat.RgbBitCount / 8;// Могут быть проблемы с двуцветными изображениями(если такие есть)
                    textureData = new LayerConverter(reader.ReadBytes(pitch * height), header.PixelFormat);
                    textures[i] = new PlanarLayer(textureData.Data, width, height, textureData.Format);
                }
            }
            else
            {
                throw new ApplicationException("Формат неподдерживается.");
                /*// С компресией
                // Каждый следующий мипмап занимает в два раза меньше места
                // Проверяем наличие линейного размера                
                int minimumSize = 8;
                if (((header.PixelFormat.FourCC >> 24) & 0x000000FF) > (int)'1') minimumSize = 16;
                int linearSize = header.PitchOrLinearSize;
                if ((header.Flags & Header.DDSD_LINEARSIZE) == 0)
                {
                    linearSize = width * height * minimumSize;
                }
                pixelFormat = "";
                pixelFormat += new string((char)((header.PixelFormat.FourCC) & 0x000000FF), 1);
                pixelFormat += new string((char)((header.PixelFormat.FourCC >> 8) & 0x000000FF), 1);
                pixelFormat += new string((char)((header.PixelFormat.FourCC >> 16) & 0x000000FF), 1);
                pixelFormat += new string((char)((header.PixelFormat.FourCC >> 24) & 0x000000FF), 1);
                // Грузим основную карту
                mainLayer = new RawImage(width, height, reader.ReadBytes(linearSize));
                // Грузим мипы
                for (int i = 0; i < mipMapsCount; i++)
                {
                    width /= 2;
                    height /= 2;
                    if (width == height) linearSize /= 2;
                    else linearSize = width * height * minimumSize;
                    if (width == 0) width = 1;
                    if (height == 0) height = 1;
                    if (linearSize < minimumSize) linearSize = minimumSize;
                    textures[i] = new RawImage(width, height, reader.ReadBytes(linearSize));
                }*/
            }
            return new PlanarTexture(mainLayer, textures);
        }

        /// <summary>
        /// Загрузка кубемапа
        /// </summary>
        /// <param name="header">Заголовок</param>
        /// <param name="reader">Ридер с данными</param>
        CubicTexture LoadCubeMap(Header header, BinaryReader reader)
        {
            // Извлекаем нужные данные из заголовка
            int width = header.Width;
            int height = header.Height;
            bool hasMipMaps = (((header.Flags & Header.DDSD_MIPMAPCOUNT) != 0) && ((header.Caps.Caps1 & Header.DDSCAPS_MIPMAP) != 0) && ((header.Caps.Caps1 & Header.DDSCAPS_COMPLEX) != 0));
            int mipMapsCount = 0;

            PlanarTexture positiveX = null;
            PlanarTexture positiveY = null;
            PlanarTexture positiveZ = null;
            PlanarTexture negativeX = null;
            PlanarTexture negativeY = null;
            PlanarTexture negativeZ = null;

            if (hasMipMaps) mipMapsCount = header.MipMapCount - 1;
            // Положительный X
            if ((header.Caps.Caps2 & Header.DDSCAPS2_CUBEMAP_POSITIVEX) != 0) positiveX = LoadPlanarTexture(header, reader);
            // Отрицательный X
            if ((header.Caps.Caps2 & Header.DDSCAPS2_CUBEMAP_NEGATIVEX) != 0) negativeX = LoadPlanarTexture(header, reader);
            // Положительный Y
            if ((header.Caps.Caps2 & Header.DDSCAPS2_CUBEMAP_POSITIVEY) != 0) positiveY = LoadPlanarTexture(header, reader);
            // Отрицательный Y
            if ((header.Caps.Caps2 & Header.DDSCAPS2_CUBEMAP_NEGATIVEY) != 0) negativeY = LoadPlanarTexture(header, reader);
            // Положительный Z
            if ((header.Caps.Caps2 & Header.DDSCAPS2_CUBEMAP_POSITIVEZ) != 0) positiveZ = LoadPlanarTexture(header, reader);
            // Отрицательный Z
            if ((header.Caps.Caps2 & Header.DDSCAPS2_CUBEMAP_NEGATIVEZ) != 0) negativeZ = LoadPlanarTexture(header, reader);

            
            if (positiveX == null) positiveX = negativeX;
            if (positiveY == null) positiveY = negativeY;
            if (positiveZ == null) positiveZ = negativeZ;
            if (negativeX == null) negativeX = positiveX;
            if (negativeY == null) negativeY = positiveY;
            if (negativeZ == null) negativeZ = positiveZ;

            return new CubicTexture(positiveX, positiveY, positiveZ, negativeX, negativeY, negativeZ);
        }

        #endregion
    }
}
